<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="内存分配算法实现,第二次实验报告,动态内存分配表,框架:jQuery1.8.3">
    <meta name="author" content="蔡申傲">
    <title>内存分配算法的实现</title>
    <script src="../js/jquery-1.8.3.min.js"></script>
    <style>
        *{
            padding:0px;
            margin: 0px;
        }
        body{
            /* perspective: 1000px; */
            display: flex;
            align-items: center;
            flex-direction: column;
        }
        .inputArea h2{
            color: red;
            text-align: center;
            margin-top: 10px;
            margin-bottom: 5px;
            font-size: 24px;
        }
        .displayData{
            display: flex;
            flex-direction: row;
            flex-grow: 1;
            text-align: center;
        }
        .displayData th{
            width: 100px;
        }
        .displayData table{
            margin-left: 10px;
        }
        .dataVisualization{
            width: 600px;
            height: 500px;
            background-color: red;
        }
        .dataVisualization .memory{
            float: right;
            width: 200px;
            height: 450px;
            margin: 25px 20px;
            background-color: green;
            position: relative;
        }
        /* .memory{
            transform-style: preserve-3d;设置3D场景风格
        } */
        .memory .processFarther{
            position: absolute;
            top: 0px;
            left: 0px;        
            transform: translate(-370px,114px);
            width: 200px;
            height: 200px;
            background-color: black;
        }
        .process{
            position: absolute;
            width: 200px;
            transition: all 2s;
            top: 0px;
            background-color: skyblue;
        }
        .memory .block{
            position: absolute;
            width:200px;
            text-align: center;
            transition: all 2s;
            top: 0px;
            background-color: blue;
        }
        /* .block:hover{
            transition: all .3s ease-in-out;
            transform: scale(1.05);
        } */
        
    </style>
</head>
<body>
    <div class="inputArea">
        <h2>进程操作</h2>
        <table border="1px">
            <thead>
                <tr>
                    <th>进程名</th>
                    <th>进程大小</th>
                    <th colspan="3">添加</th>
                    <th>删除</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>
                        <input type="text" id="processName" >
                    </td>
                    <td>
                        <input type="number" id="processSize">
                    </td>
                    <td>
                        <button id="first">首次适应</button>
                    </td>
                    <td>
                        <button id ="best">最佳适应</button>
                    </td>
                    <td>
                        <button id="worst">最坏适应</button>
                    </td>
                    <td>
                        <button id="delete">删除</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
    <h2>数据展示</h2>
    <div class="displayData">
        <div class="dataVisualization">
            <div class="memory">
                <div class="processFarther"></div>
            </div>
        </div>
        <table border="1px">
            <thead>
                <tr>
                    <th>区块名</th>
                    <th>区块大小</th>
                    <th>地址</th>
                    <th>状态</th>
                </tr>
            </thead>
            <tbody id="displayBlock"></tbody>
        </table>
        <table border="1px">
            <thead>
                <tr>
                    <th>进程名</th>
                    <th>进程大小</th>
                    <th>地址</th>
                </tr>
            </thead>
            <tbody id="displayProcess"></tbody>
        </table>
    </div>
   <script>
       /**
        * 定义全局变量部分
       */
        const Memory = {};
        Memory.size = 1024;
        Memory.initialAddress = 0;
        Memory.blockIndex = 1;  //用于标识区块
        Memory.unitLength = 450 / Memory.size; //单位长度
        Memory.blockList = new Array();
        Memory.processList = new Array();
        //初始化
        init();
        displayTableData();
        /**
         * 接收用户输入部分
        */
       //首次适应
        $('#first').click(function(){
            //判断两个输入框是否为空，两个输入框都不能为空
            if($('#processName').val() === '' || $('#processSize').val() === ''){
                alert('进程名和进程大小都不能为空');
                return;
            }
            //判断输入的进程大小是否合法
            if($('#processSize').val() <= 0){
                alert("输入的进程大小不能小于等于0");
                return;
            }
            //判断进程名是否唯一
            if(!retrieve($('#processName').val())){
                alert('该进程名已存在');
                return;
            }
            var process = createProcess($('#processName').val(),$('#processSize').val());
            firstAdaptation(process);
            //将输入框的内容赋值为空
            $('#processName').val('');
            $('#processSize').val('');
        });
        //最佳适应
        $('#best').click(function(){
            //判断两个输入框是否为空，两个输入框都不能为空
            if($('#processName').val() === '' || $('#processSize').val() === ''){
                alert('进程名和进程大小都不能为空');
                return;
            }
            //判断输入的进程大小是否合法
            if($('#processSize').val() <= 0){
                alert("输入的进程大小不能小于等于0");
                return;
            }
            //判断进程名是否唯一
            if(!retrieve($('#processName').val())){
                alert('该进程名已存在');
                return;
            }
            var process = createProcess($('#processName').val(),$('#processSize').val());
            bestFit(process);
            
            //将输入框的内容赋值为空
            $('#processName').val('');
            $('#processSize').val('');
        });
        //最坏适应
        $('#worst').click(function(){
            //判断两个输入框是否为空，两个输入框都不能为空
            if($('#processName').val() === '' || $('#processSize').val() === ''){
                alert('进程名和进程大小都不能为空');
                return;
            }
            //判断输入的进程大小是否合法
            if($('#processSize').val() <= 0){
                alert("输入的进程大小不能小于等于0");
                return;
            }
            //判断进程名是否唯一
            if(!retrieve($('#processName').val())){
                alert('该进程名已存在');
                return;
            }
            var process = createProcess($('#processName').val(),$('#processSize').val());
            worstFit(process);
            //将输入框的内容赋值为空
            $('#processName').val('');
            $('#processSize').val('');
        });
        //删除
        $('#delete').click(function(){
            //判断两个输入框是否为空，两个输入框都不能为空
            if($('#processName').val() === ''){
                alert('进程名不能为空');
                return;
            }
            var name = $('#processName').val();
            if(!removeProcess(name)){
                alert('删除失败');
            }
            displayTableData();
            //将输入框的内容赋值为空
            $('#processName').val('');
            $('#processSize').val('');
        });

        /**
         * 前端显示相关函数
        */
        //显示增加区块
        function displayAddBlock(block){
            block.showHeight = block.size * Memory.unitLength;
            block.showTop = block.initialAddress * Memory.unitLength;
            $('.memory').append('<div class="block" style="top:'+block.showTop+'px;" id='+block.name+'>'+block.name+'</div>');
            $('#'+block.name).animate({
                'height':block.showHeight+'px',
                'font-size':block.showHeight /10  +'px',
                'line-height':block.showHeight +'px',
            });
            //为区块添加鼠标样式
            mouseStyle(block.name,2);
        }
        //显示添加进程
        function displayAddProcess(process,block){
            //对区块的显示数据进行计算
            block.showHeight = block.size * Memory.unitLength;
            block.showTop = block.initialAddress * Memory.unitLength;
            //对进程的显示数据进行计算
            process.showHeight = process.size * Memory.unitLength;
            process.showTop = process.initialAddress * Memory.unitLength;
            //对应的区块位置和高度进行缩小
            $('#'+block.name).animate({
                'height':block.showHeight+'px',
                'font-size':block.showHeight /10  +'px',
                'line-height':block.showHeight +'px',
                'top':block.showTop+'px'
            },function(){
                //判断block的大小是否为0
                if(block.size <= 0){
                    //删除对应的区块
                    $('.memory').remove('#'+block.name);
                }
                $('.processFarther').append('<div class="process" id='+process.name+'>'+process.name+'</div>');
                $('#'+process.name).animate({     
                'height':process.showHeight+'px',
                'font-size':process.showHeight /10  +'px',
                'line-height':process.showHeight +'px',
                'top':process.showTop+'px',
                },function(){
                    $('#'+process.name).css({
                        'transform': 'translate(370px,-114px)'
                    });
                });
                //为该进程块添加鼠标样式
                mouseStyle(process.name,1);
            });
        }
        //为传入的id添加鼠标样式 参数1为进程  2为区块
        function mouseStyle(ID ,parameter){
            if(parameter == 1){
                $('#'+ID).mouseover(function(){
                    $('#'+ID).css({
                        'transition': 'all .3s ease-out',
                        'transform': 'scale(1.02) translate(360px,-114px)'
                    });
                });
                $('#'+ID).mouseout(function(){
                    $('#'+ID).css({
                        'transform': 'scale(1) translate(370px,-114px)',
                    });
                    setTimeout(function(){
                        $('#'+ID).css({
                            'transition': 'all 2s',
                        })
                    },300);
                });
            }
            if(parameter == 2){
                $('#'+ID).mouseover(function(){
                    $('#'+ID).css({
                        'transition': 'all .3s ease-out',
                        'transform': 'scale(1.02)'
                    });
                });
                $('#'+ID).mouseout(function(){
                    $('#'+ID).css({
                        'transform': 'scale(1)',
                    });
                    setTimeout(function(){
                        $('#'+ID).css({
                            'transition': 'all 2s',
                        })
                    },300);
                });
            }
            
        }
        //显示移除进程
        function displayRemovalProcess(process){
            //对进程的显示数据进行计算
            process.showHeight = process.size * Memory.unitLength;
            process.showTop = process.initialAddress * Memory.unitLength;
            $('#'+process.name).animate({     
                'height':'0px',
                'font-size':+'0px',
                'line-height':'0px',
                'top':'0px',
                },function(){
                    $('#'+process.name).css({
                        'transform': 'translate(0px,0px)',
                    });
                    setTimeout(function(){
                        $('.processFarther #'+process.name).remove();
                    },2000)
                });
            
        }
        //显示改变区块
        function displayChangeBlock(block){
            //计算显示的高 和 top
            block.showHeight = block.size * Memory.unitLength;
            block.showTop = block.initialAddress * Memory.unitLength;
            $('#'+block.name).animate({
                'height':block.showHeight+'px',
                'font-size':block.showHeight /10  +'px',
                'line-height':block.showHeight +'px',
                'top':block.showTop+'px',
            });
        }
        //显示表格数据
        function displayTableData(){
            //先清空表格中的数据
            $('#displayBlock').empty();
            $('#displayProcess').empty();
            //添加数据
            //添加区块数据
            Memory.blockList.forEach(block=>{
                $('#displayBlock').append('<tr><td>'+block.name+'</td><td>'+block.size+'</td><td>'+block.initialAddress+'</td><td>'+block.staus+'</td></tr>');
            });
            Memory.processList.forEach(process=>{
                $('#displayProcess').append('<tr><td>'+process.name+'</td><td>'+process.size+'</td><td>'+process.initialAddress+'</td></tr>');
            });
        }

        /**
         * 内存分配算法实现部分
        */
        //检索id 因为HTML中id是唯一的，所以在添加进程时，进程名不能重复
        function retrieve(name){
            Memory.blockList.forEach(block=>{
                if(block.name == name){
                    return false;
                }
            })
            Memory.processList.forEach(process=>{
                if(process.name == name){
                    return false;
                }
            })
            return true;
        }

        //先将内存初始化为一个区块
        function init(){
            var initBlock = {};
            initBlock.name = "区块"+Memory.blockIndex++;
            initBlock.size = Memory.size;
            initBlock.initialAddress = Memory.initialAddress;
            initBlock.staus = "空闲";
            Memory.blockList.push(initBlock);
            //调用显示增加区块的函数
            displayAddBlock(initBlock);
        }
        //创建进程
        function createProcess(name,size){
            var process = {};
            process.name = name;
            process.size = size*1;
            return process;
        }
        //首次适应算法
        function firstAdaptation(process){
            //先将区块按照地址递增的方式排列
            Memory.blockList.sort(compareA('initialAddress'));
            if(addProcess(process)){
                displayTableData();
            }else{
                alert("添加失败");
            }
        }
        //最佳适应算法
        function bestFit(process){
            //先将区块集合按照区块大小递增排序
            Memory.blockList.sort(compareA('size'),true);
            if(addProcess(process)){
                displayTableData();
            }else{
                alert("添加失败");
            }
        }
        //最坏适应算法
        function worstFit(process){
            Memory.blockList.sort(compare('size'));
            if(addProcess(process)){
                displayTableData();
            }else{
                alert("添加失败");
            }
        }
        //移除进程
        function removeProcess(name){
            var process = {};
            for(var i =0;i<Memory.processList.length;i++){
                process = Memory.processList[i];
                if(process.name == name){
                    //找到要移除的进程
                    //创建一个与进程同大的区块
                    var block = {};
                    //创建的区块的起始地址 = 要移除进程的起始地址
                    block.initialAddress = process.initialAddress;
                    //创建的区块的大小 = 要移除的进程的大小
                    block.size = process.size;
                    //动态创建区块
                    addBlock(block);
                    //前端移除对应的div
                    displayRemovalProcess(process);
                    //在processList中移除进程
                    removeByValue(Memory.processList,'name',process.name);
                    return true;
                }
            }
        }
        //添加区块
        function addBlock(addBlock){
            var block = {};
            //先将区块按照地址递增的方式排列
            Memory.blockList.sort(compareA('initialAddress'));
            for(var i =0;i<Memory.blockList.length;i++){
                block = Memory.blockList[i];
                var temp;
                var endAddress = block.initialAddress + block.size;
                //判断新建区块能否与前面的区块合并
                if(endAddress == addBlock.initialAddress){
                    //可以
                    temp = addBlock.size;
                    //增大区块的大小
                    block.size += temp;
                    //判断是否与后面的区块挨着，如果挨着，合并
                    // if(i < Memory.blockList.length-1){
                        var tempBlock = Memory.blockList[i+1];  //获取到后一个区块
                        temp = addBlock.initialAddress + addBlock.size;
                        if(temp == tempBlock.initialAddress){
                            var size = tempBlock.size;
                            block.size += size;
                            //移除要增加的区块紧挨着的高地址的区块
                            $('.memory #'+tempBlock.name).remove();
                            //将移除过程前端显示
                            removeByValue(Memory.blockList,'name',tempBlock.name);
                        }
                    //通过前端 动态显示
                    displayChangeBlock(block);
                    return;
                }
                //判断新建的区块能否与后面的区块合并
                endAddress = addBlock.initialAddress + addBlock.size;
                if(endAddress == block.initialAddress){
                    //可以
                    temp = addBlock.size;
                    //减小起始地址
                    block.initialAddress -= temp;
                    block.size += temp;
                    //通过前端 动态显示
                    displayChangeBlock(block);
                    return;
                }
            }
            //新建区块既不能与前面的合并  也不能与后面的区块合并
            //向集合中添加新的区块 完善信息
            addBlock.staus = "空闲";
            addBlock.name = "区块"+Memory.blockIndex++;
            Memory.blockList.push(addBlock);
            //通过前端 添加区块
            displayAddBlock(addBlock);
        }       
        //添加进程
        function addProcess(process){
            var block ={};
            for(var i =0;i<Memory.blockList.length;i++){
                block = Memory.blockList[i];
                if(block.size >= process.size){//该区块允许进程添加
                    //完善进程信息
                    process.staus = "运行态";
                    process.initialAddress = block.initialAddress;
                    //对区块的信息进行修改
                    block.size -= process.size;
                    block.initialAddress += process.size;
                    //前端显示，符合条件的区块缩小、改变top，创建进程块补上缺块
                    displayAddProcess(process,block);
                    if(block.size <= 0){
                        //区块大小为0时,移除该区块
                        removeByValue(Memory.blockList,'name',block.name);
                    }
                    Memory.processList.push(process);
                    return true;
                }
            }
            return false;
        }   
            
        /**
         * 网络上找的工具函数
        */

        /*
        * 根据数组对象属性删除对应项
        * @param {Array} arr - 数组对象
        * @param {String} attr - 属性
        * @param {} value - 属性值
        * @return void
        */
        function removeByValue(arr, attr, value){ 
            var index=0;
            for(var i in arr){
                if(arr[i][attr]==value){
                index=i;
                break;
                }
            }
            arr.splice(index,1);
        }
        //升序
        function compareA(property){
            return function(a,b){
                var value1 = a[property];
                var value2 = b[property];
                return value1 - value2;
            }
        }
        //降序
        function compare(property){
            return function(a,b){
                var value1 = a[property];
                var value2 = b[property];
                return value2 - value1;
            }
        }
   </script>
</body>
<script>
    console.log("本程序有作者：蔡申傲个人制作，对数组中元素的删除、排序来源于网络，所有权归作者本人所有....");
</script>
</html>